package com.future.spring.io;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * @author weber
 * @Description:
 * @Date 2025/7/30 15:20
 */
public class ResourceResolver {

    String basePackage;

    public ResourceResolver(String basePackage) {
        this.basePackage = basePackage;
    }

    public <R> List<R> scan(Function<Resour, R> mapper) {
        String basePackagePath = this.basePackage.replace(".", "/");
        String path = basePackagePath;
        try {
            List<R> res = new ArrayList<>();
            scan0(basePackage, path, res, mapper);
            return res;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private <R> void scan0(String basePackage, String path, List<R> list, Function<Resour, R> mapper) throws IOException, URISyntaxException {
        Enumeration<URL> en = getContextClassLoader().getResources(path);
        while (en.hasMoreElements()) {
            URL url = en.nextElement();
            URI uri = url.toURI();
            String uriStr = removeTrailingSlash(uriToString(uri));
            String uriBaseStr = uriStr.substring(0, uriStr.length() - basePackage.length());
            if (uriBaseStr.startsWith("file:")) {
                uriBaseStr = uriBaseStr.substring(5);
            }
            if (uriBaseStr.startsWith("jar:")) {
                scanFile(true, uriBaseStr, jarUriTOPath(basePackage, uri), list, mapper);
            } else {
                scanFile(false, uriBaseStr, Paths.get(uri), list, mapper);
            }
            System.out.println(uriStr);
        }
    }

    private Path jarUriTOPath(String basePackage, URI uri) throws IOException {
        return FileSystems.newFileSystem(uri, Map.of()).getPath(basePackage);
    }

    private <R> void scanFile(boolean isJar, String base, Path root, List<R> collector, Function<Resour, R> mapper) throws IOException {
        String baseDir = removeTrailingSlash(base);
        Files.walk(root).filter(Files::isRegularFile).forEach(file -> {
            Resour res;
            if (isJar) {
                res = new Resour(baseDir, removeTrailingSlash(file.toString()));
            } else {
                String path = file.toString();
                String name = removeTrailingSlash(path.substring(baseDir.length()));
                res = new Resour("file:" + path, name);
            }
            R r = mapper.apply(res);
            if (r != null) {
                collector.add(r);
            }
        });
    }

    private String removeTrailingSlash(String s) {
        if (s.endsWith("/") || s.startsWith("\\")) {
            s = s.substring(1);
        }
        return s;
    }

    private String uriToString(URI uri) {
        return URLDecoder.decode(uri.toString(), StandardCharsets.UTF_8);
    }

    ClassLoader getContextClassLoader() {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        if (cl == null) {
            cl = this.getClass().getClassLoader();
        }
        return cl;
    }
}
